package idv.mylee.web.struts;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.opensymphony.xwork2.ObjectFactory;

@SuppressWarnings("unchecked")
public class TidyObjectFactory extends ObjectFactory {
	private static final long serialVersionUID = 1L;
	private final static String TIDY_BEAN_PREFIX = "tidy:";
	private final static int TIDY_BEAN_PREFIX_LENGTH = TIDY_BEAN_PREFIX.length();
	private final static String ACTION_BEAN_PROPERTIES_FILE = "/action-bean.properties";
	private static Map<String,Object>beans = new ConcurrentHashMap<String,Object>();
	private static Map<String,String>definitions = new TreeMap<String,String>();
	private static final Log log = LogFactory.getLog(TidyObjectFactory.class);
			
	static {
		Properties properties = new Properties();
		InputStream is = TidyObjectFactory.class.getResourceAsStream(ACTION_BEAN_PROPERTIES_FILE);
		try {
			properties.load(is);
			copy(properties, definitions);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public Object buildBean(String className, Map<String, Object> extraContext,
			boolean injectInternal) throws Exception {
		log.info("buildBean: " + className);
		return super.buildBean(className, extraContext, injectInternal);
	}

	@Override
	public Class getClassInstance(String className)	throws ClassNotFoundException {
		log.info("getClassInstance: " + className);
		if (className.startsWith(TIDY_BEAN_PREFIX)) {
			try {
				Object o = getBean(className.substring(TIDY_BEAN_PREFIX_LENGTH));
				return o.getClass();
			} catch (Exception e) {
			}
		}
		return super.getClassInstance(className);
	}


	public static Object getBean(String beanName) throws Exception {
		Object bean = beans.get(beanName);
		if (bean!=null)
			return bean;
		synchronized(TidyObjectFactory.class) {
			bean = beans.get(beanName);
			if (bean!=null)
				return bean;
			bean = createBean(beanName);
			beans.put(beanName, bean);
			return bean;
		}
	}

	private static Object createBean(String beanName) throws Exception {
		log.info("Creating bean: " + beanName);
		String definition = definitions.get(beanName);
		if (definition==null) {
			if (beanName.contains("."))
				throw new RuntimeException("Format error: bean " + beanName + " looks like a Class, try to add a colon before it.");
			throw new RuntimeException("");
		}
		
		String s[] = definition.split(",");
		String className = s[0].trim();
		if (className.charAt(0)!=':')
			return getBean(className);
		className = className.substring(1);
		Object bean = Class.forName(className).newInstance();
		for (int i=1; i<s.length; i++) {
			String propertyName = s[i].trim();
			int idx = propertyName.indexOf(':');
			beanName = propertyName;
			if (idx>=0) {
				propertyName = beanName.substring(0, idx);
				beanName = beanName.substring(idx+1);
			}
			Object propertyBean = getBean(beanName);
			PropertyUtils.setProperty(bean, propertyName, propertyBean);
		}
		return bean;
	}

	private static void copy(Properties properties, Map<String,String>map) {
		for (Map.Entry entry : properties.entrySet())
			map.put((String)entry.getKey(), (String)entry.getValue());
	}
}
